package site.lianwu.mybatis.plus.query.processor;

import site.lianwu.mybatis.plus.query.model.QueryClass;

import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import java.util.function.Function;

/**
 * 安全查询类处理器，提取查询类时检测可能存在的循环引用
 * @author LianWu
 * @since 1.1.0
 */
public class SecureQueryClassProcessor extends QueryClassProcessorDecorator {

    private static final ThreadLocal<ProcessingRecord> processingRecord = ThreadLocal.withInitial(ProcessingRecord::new);

    public SecureQueryClassProcessor(Collection<QueryFieldProcessor> queryFieldProcessors) {
        super(queryFieldProcessors);
    }

    public SecureQueryClassProcessor(Function<Collection<QueryFieldProcessor>, QueryClassProcessor> queryClassProcessorProvider) {
        super(queryClassProcessorProvider);
    }

    public SecureQueryClassProcessor(Collection<QueryFieldProcessor> queryFieldProcessors, Function<Collection<QueryFieldProcessor>, QueryClassProcessor> queryClassProcessorProvider) {
        super(queryFieldProcessors, queryClassProcessorProvider);
    }

    /**
     * 提取查询类
     * <p>检测是否存在循环引用：记录已处理的类集合，若在提取前该类已在集合中，则表明存在循环引用</p>
     *
     * @param clz 条件对象所属类
     * @param includeInheritedFields 是否包含继承字段
     * @return 查询类 {@link QueryClass}
     * @throws ProcessingException 如果检测到循环引用
     */
    @Override
    public QueryClass extract(Class<?> clz, boolean includeInheritedFields) {
        ProcessingRecord record = processingRecord.get();
        if (!record.classSet.add(clz)) {
            throw new ProcessingException("提取查询类时检测到循环引用：" + clz.getName());
        }
        ++record.depth;
        try {
            return queryClassProcessor.extract(clz, includeInheritedFields);
        } finally {
            if (--record.depth == 0) {
                processingRecord.remove();
            }
        }
    }

    private static class ProcessingRecord {

        Set<Class<?>> classSet = new HashSet<>();
        int depth = 0;

    }

}
